from contextlib import asynccontextmanager

from fastapi import FastAPI, Request
from fastapi.exceptions import RequestValidationError
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import JSONResponse
from icecream import ic
from starlette.exceptions import HTTPException as StarletteHTTPException
from tortoise import Tortoise

from app.config.setting import settings
from app.constants.redis_key import RedisKey
from app.jobs.schedule import scheduler, scheduler_init
from app.libs.base_view.base_manager import BaseManager, Managers
from app.libs.base_view.base_model import BaseOrmModel, CheckingModel
from app.libs.base_view.mgr_api_generator import ManagerApiGenerator
from app.libs.base_view.utils import get_classes_from_package
from app.libs.cache.redis import get_redis_conn, init_redis
from app.libs.logger import aio_logger, logger_init
from app.libs.utils import GetSetTer
from app.routers import api_router


@asynccontextmanager
async def lifespan(_app: FastAPI):
    # 导入路由文件
    _app.include_router(api_router, prefix='')

    await logger_init()

    await ic_init()

    # 初始化MySQL数据库
    await db_init()

    # 初始化Redis数据库
    await init_redis()

    # 初始化定时任务
    await scheduler_init()

    # Yield control back to FastAPI to start the application
    yield

    # Shutdown logic (after yield)
    await aio_logger.info('关闭应用...')
    await aio_logger.shutdown()
    scheduler.shutdown()
    await Tortoise.close_connections()
    await aio_logger.info('关闭应用成功.')


# 创建fastapi实例
app = FastAPI(
    title=settings.project_name,
    openapi_url='/openapi.json' if settings.debug else None,
    root_path='/api' if settings.environment == 'dev' else None,
    docs_url='/docs' if settings.debug else None,
    redoc_url='/redoc' if settings.debug else None,
    description='''
测试环境：https://dev.test.com/api/ \n
生产环境：https://www.test.com/api/
    ''',
    lifespan=lifespan
)

if settings.environment in ['dev', 'local']:
    allow_origins = ['*']
    app.add_middleware(
        CORSMiddleware,
        allow_origins=allow_origins,  # 允许的域名列表
        allow_credentials=True,
        allow_methods=['*'],  # 允许的方法列表
        allow_headers=['*'],  # 允许的头列表
    )


@app.exception_handler(StarletteHTTPException)
async def http_exception_handler(request: Request, exc):
    """重写 HTTPException 错误处理器"""

    async def get_content(exc):
        print(exc)
        if isinstance(exc.detail, dict):
            content = {'code': exc.detail.get('code', exc.status_code), 'message': exc.detail.get('message')}
        else:
            content = {'code': exc.status_code, 'message': exc.detail}
        return content

    return JSONResponse(
        status_code=exc.status_code,
        content=await get_content(exc)
    )


@app.exception_handler(RequestValidationError)
async def validation_exception_handler(request: Request, exc: RequestValidationError):
    """重写入参验证 错误处理器"""
    await aio_logger.info(f'请求参数验证错误：{exc.errors()}')
    return JSONResponse(
        status_code=422,
        content={
            "code": 422000,
            "message": "; ".join([f"{err['loc'][1] if len(err['loc']) > 1 else err['loc'][0]}: {err['msg']}" for err in exc.errors()]),
        },
    )


@app.middleware('http')
async def http_request_middleware(request: Request, call_next):
    """
    所有请求的中间件
    :param request:
    :param call_next:
    :return:
    """
    # 获取自定义请求头 X-Language，# 默认为简体
    language = request.headers.get("X-Language", "zh-CN").lower()

    # 处理请求并获取响应
    response = await call_next(request)
    return response


async def ic_init():
    ic.configureOutput(prefix=f'{settings.service_name}|')
    if settings.debug:
        ic.enable()
    else:
        ic.disable()


async def db_init():
    db_config = {
        "connections": {
            "default": f'mysql://{settings.mysql_user}:{settings.mysql_password}@{settings.mysql_host}:{settings.mysql_port}/{settings.mysql_database}'
        },
        "apps": {
            "models": {
                "models": ["app.models"],
                "default_connection": "default",
            }
        }
    }
    await Tortoise.init(config=db_config)
    await aio_logger.info('初始化数据库连接成功！')
